﻿using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Experimental.Rendering;
using Conditional = System.Diagnostics.ConditionalAttribute;

public class MyPipeline : RenderPipeline 
{
    DrawRendererFlags drawFlags;

public MyPipeline (bool dynamicBatching, bool instancing) {

		GraphicsSettings.lightsUseLinearIntensity = true;
        drawFlags = DrawRendererFlags.None;
        
		if (dynamicBatching) {
			drawFlags = DrawRendererFlags.EnableDynamicBatching;
		}
		if (instancing) {
			drawFlags |= DrawRendererFlags.EnableInstancing;
		}
	}
    
	public override void Render (ScriptableRenderContext renderContext, Camera[] cameras)
    {
		base.Render(renderContext, cameras);

        foreach (var camera in cameras) {
			Render(renderContext, camera);
		}
        
	}
    CullResults cull;
    CommandBuffer cameraBuffer = new CommandBuffer {
		name = "Render Camera"
	};

#region lighting
	const int maxVisibleLights = 4;
	
	static int visibleLightColorsId = Shader.PropertyToID("_VisibleLightColors");
	static int visibleLightDirectionsOrPositionsId  = Shader.PropertyToID("_VisibleLightDirectionsOrPositions");
	
	Vector4[] visibleLightColors = new Vector4[maxVisibleLights];
	Vector4[] visibleLightDirectionsOrPositions = new Vector4[maxVisibleLights];
#endregion

	void ConfigureLights () {
		int i = 0;
		for (; i < cull.visibleLights.Count; i++) {
			if (i == maxVisibleLights) {
				break;
			}
			VisibleLight light = cull.visibleLights[i];
			visibleLightColors[i] = light.finalColor;

			if (light.lightType == LightType.Directional) {
				Vector4 v = light.localToWorld.GetColumn(2);
				v.x = -v.x;
				v.y = -v.y;
				v.z = -v.z;
				visibleLightDirectionsOrPositions[i] = v;
			}
			else {
				visibleLightDirectionsOrPositions[i] =
					light.localToWorld.GetColumn(3);
			}
		}
		for (; i < maxVisibleLights; i++) {
			visibleLightColors[i] = Color.clear;
		}
	}

    void Render (ScriptableRenderContext context, Camera camera) {
		
        context.SetupCameraProperties(camera);

        ScriptableCullingParameters cullingParameters;
        if (!CullResults.GetCullingParameters(camera, out cullingParameters)) {
			return;
		}
#if UNITY_EDITOR
        if (camera.cameraType == CameraType.SceneView) {
			ScriptableRenderContext.EmitWorldGeometryForSceneView(camera);
		}
#endif
		CullResults.Cull(ref cullingParameters, context, ref cull);

        //var buffer = new CommandBuffer{name=camera.name};

        CameraClearFlags clearFlags = camera.clearFlags;
        cameraBuffer.name = camera.name;

		cameraBuffer.ClearRenderTarget(
			(clearFlags & CameraClearFlags.Depth) != 0,
			(clearFlags & CameraClearFlags.Color) != 0,
			camera.backgroundColor
		);

		ConfigureLights();

        cameraBuffer.BeginSample(camera.name);

		
		cameraBuffer.SetGlobalVectorArray(visibleLightColorsId, visibleLightColors);
		cameraBuffer.SetGlobalVectorArray(visibleLightDirectionsOrPositionsId, visibleLightDirectionsOrPositions);

        //cameraBuffer.EndSample("Render Camera");
		context.ExecuteCommandBuffer(cameraBuffer);
		cameraBuffer.Clear();

		var drawSettings = new DrawRendererSettings(camera, new ShaderPassName("SRPDefaultUnlit"));
        drawSettings.flags = drawFlags;
        drawSettings.sorting.flags = SortFlags.CommonOpaque;

		var filterSettings = new FilterRenderersSettings(true){renderQueueRange=RenderQueueRange.opaque};


        //渲染不透明物体
		context.DrawRenderers(
			cull.visibleRenderers, ref drawSettings, filterSettings
		);

        //渲染背景
        context.DrawSkybox(camera);

        //渲染透明物体
        drawSettings.sorting.flags = SortFlags.CommonTransparent;
        filterSettings.renderQueueRange = RenderQueueRange.transparent;
        context.DrawRenderers(
			cull.visibleRenderers, ref drawSettings, filterSettings
		);

        DrawDefaultPipeline(context, camera);

        cameraBuffer.EndSample(camera.name);

		context.ExecuteCommandBuffer(cameraBuffer);
		cameraBuffer.Clear();

		context.Submit();
	}

    Material errorMaterial;
    [Conditional("DEVELOPMENT_BUILD"), Conditional("UNITY_EDITOR")]
    void DrawDefaultPipeline(ScriptableRenderContext context, Camera camera) {
        
        if (errorMaterial == null) {
			Shader errorShader = Shader.Find("Hidden/InternalErrorShader");
			errorMaterial = new Material(errorShader) {
				hideFlags = HideFlags.HideAndDontSave
			};
		}

		var drawSettings = new DrawRendererSettings(
			camera, new ShaderPassName("ForwardBase")
		);
        drawSettings.SetShaderPassName(1, new ShaderPassName("PrepassBase"));
		drawSettings.SetShaderPassName(2, new ShaderPassName("Always"));
		drawSettings.SetShaderPassName(3, new ShaderPassName("Vertex"));
		drawSettings.SetShaderPassName(4, new ShaderPassName("VertexLMRGBM"));
		drawSettings.SetShaderPassName(5, new ShaderPassName("VertexLM"));
        drawSettings.SetOverrideMaterial(errorMaterial, 0);
		
		var filterSettings = new FilterRenderersSettings(true);
		
		context.DrawRenderers(
			cull.visibleRenderers, ref drawSettings, filterSettings
		);
	}
}
